

import { BaseWsClient, BaseWsClientOptions } from "tsrpc-base-client";
import { ServiceProto } from "tsrpc-proto";

import { serviceProto as gameServiceProto, ServiceType as gameServiceType } from "./protocols/serviceProto";
import { AWsClient } from "../tsgf/AClient";
import { logger } from "../tsgf/logger";
import { Result } from "../tsgf/Result";

export class GameClient extends AWsClient<gameServiceType>{
    constructor(buildClient: (proto: ServiceProto<gameServiceType>, options?: Partial<BaseWsClientOptions>) => BaseWsClient<gameServiceType>,
        serverUrl: string) {
        super(buildClient, gameServiceProto, {
            server: serverUrl,
            json: false,
            logger: logger,
        });
        this.connectionId = "";
        //设置断线重连的中间件
        this.client.flows.postDisconnectFlow.push(async v => {
            //如果都没连上过就断开,那么忽略
            if (!this.connectionId) return v;
            //判断是否需要重连
            if (!v.isManual && this.disconnectHandler?.call(this)) {
                this.client.logger?.error('连接已断开,等待' + this.reconnectWaitSec + '秒后自动重连');
                if (this.reconnectTimerHD) clearTimeout(this.reconnectTimerHD);
                this.reconnectTimerHD = setTimeout(async () => this.startReconnect(true), this.reconnectWaitSec * 1000);
            } else {
                this.client.logger?.error('连接已断开');
            }
            return v;
        });
    }
    public connectionId: string;

    /**
     * 断线重连等待秒数
     */
    public reconnectWaitSec = 2;
    private reconnectTimerHD: any;
    /**
     * 当断线时触发,返回是否重连,是的话,reconnectWaitSec秒后重连, 未配置则不自动重连
     */
    public disconnectHandler: (() => boolean) | undefined;
    /**断线重连最终有结果时触发(终于连上了,或者返回不继续尝试了)*/
    public onReconnectResult: ((succ: boolean, err: string | null) => void) | undefined;

    public async disconnect(): Promise<void> {
        this.connectionId = '';
        await this.client.sendMsg("Disconnect", {});
        await this.client.disconnect();
        this.stopReconnect();
    }


    private stopReconnect():void{
        if (this.reconnectTimerHD) clearTimeout(this.reconnectTimerHD);
    }
    private async startReconnect(failReTry = true): Promise<boolean> {
        const result = await this.reconnect();
        // 重连也错误，弹出错误提示
        if (result.succ) {
            this.client.logger?.log('重连成功!');
            this.onReconnectResult?.call(this, true, null);
            return true;
        }
        //如果是逻辑拒绝则不需要重连
        if (!this.connectionId || result.code == 5001) failReTry = false;

        if (failReTry && this.disconnectHandler?.call(this)) {
            this.client.logger?.error('重连失败:' + result.err + ' ' + this.reconnectWaitSec + '秒后自动重连!');
            if (this.reconnectTimerHD) clearTimeout(this.reconnectTimerHD);
            this.reconnectTimerHD = setTimeout(() => this.startReconnect(failReTry), this.reconnectWaitSec * 1000);
        } else {
            this.client.logger?.error('重连失败:' + result.err);
            this.disconnect();
            this.onReconnectResult?.call(this, false, result.err);
        }
        return false;
    }

    /**
     * 使用临时分配的令牌登录
     * @param loginToken 
     * @returns  
     */
    public async login(loginToken: string): Promise<string | null> {
        const connectRet = await this.client.connect();
        if (!connectRet.isSucc) {
            return "连接失败:" + connectRet.errMsg;
        }
        const loginRet = await this.client.callApi("Login", {
            loginToken: loginToken,
        });
        if (!loginRet.isSucc) {
            this.disconnect();
            return loginRet.err.message;
        }
        this.connectionId = loginRet.res.connectionId;
        return null;
    }
    /**
     * 断线重连, 失败的话要看code,5001表示逻辑拒绝,不需要重连
     * @param loginToken 
     * @returns  
     */
    public async reconnect(): Promise<Result<null>> {
        const connectRet = await this.client.connect();
        if (!connectRet.isSucc) {
            return Result.buildErr("连接失败:" + connectRet.errMsg);
        }
        const loginRet = await this.client.callApi("Reconnect", {
            connectionId: this.connectionId,
        });
        if (!loginRet.isSucc) {
            const code = (loginRet.err.code ?? 0) as number;
            return Result.buildErr(loginRet.err.message, code);
        }
        this.connectionId = loginRet.res.connectionId;
        return Result.buildSucc(null);
    }
}

declare global {
    
    /**
     * [需要实际客户端实现的函数]获取游戏连接客户端
     * @date 2022/2/18 - 下午11:31:49
     *
     * @param {string} gateServerUrl
     * @returns {GateClient}
     */
    function getGameClient(serverUrl: string): GameClient;
}